/*
 * Copyright 1993-2010 NVIDIA Corporation.  All rights reserved.
 *
 * NVIDIA Corporation and its licensors retain all intellectual property and 
 * proprietary rights in and to this software and related documentation. 
 * Any use, reproduction, disclosure, or distribution of this software 
 * and related documentation without an express license agreement from
 * NVIDIA Corporation is strictly prohibited.
 *
 * Please refer to the applicable NVIDIA end user license agreement (EULA) 
 * associated with this source code for terms and conditions that govern 
 * your use of this NVIDIA software.
 * 
 */


// Utilities and system includes
#include <shrUtils.h>
#include <cutil_inline.h>

#include <cuda_runtime.h>

#ifdef WIN32
#define strcasecmp strcmpi
#endif

#include "tsp_common.h"

////////////////////////////////////////////////////////////////////////////////
// CPU code
////////////////////////////////////////////////////////////////////////////////
extern "C" void initQuasirandomGenerator(unsigned int table[QRNG_DIMENSIONS][QRNG_RESOLUTION]);
extern "C" void mostrar(int NCiudades, int *ruta);
////////////////////////////////////////////////////////////////////////////////
// GPU code
////////////////////////////////////////////////////////////////////////////////
extern "C" void initTable_SM10(unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION]);
extern "C" void quasirandomGenerator_SM10(float *d_Output, unsigned int seed, unsigned int N);
extern "C" void tsp_SM10(unsigned int N, int NCuidades, int optimo, int *distancias, int *w, int *wPrima);
////////////////////////////////////////////////////////////////////////////////
extern "C" void initTable_SM13(unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION]);
extern "C" void quasirandomGenerator_SM13(float *d_Output, unsigned int seed, unsigned int N);
extern "C" void tsp_SM13(unsigned int N, int NCuidades, int optimo, int *distancias, int *w, int *wPrima);
////////////////////////////////////////////////////////////////////////////////


const int N = 1048576;

int main(int argc, char **argv)
{
    // Start logs
    shrSetLogFileName ("tsp_Logs.txt");
    shrLog("%s Starting...\n\n", argv[0]);

    unsigned int tableCPU[QRNG_DIMENSIONS][QRNG_RESOLUTION];

    int *distancias_h, *w_h;
    int *distancias_d, *w_d, *wPrima_d;
    int dim, pos;
    double delta, ref, sumDelta, sumRef, L1norm, gpuTime;
    unsigned int hTimer;
		int NCiudades, optimo;
		size_t sizeArreglo;
    size_t sizeMatriz;

		//VERIFICANDO CONSISTENCIA DE DATOS
    if(sizeof(INT64) != 8){
        shrLog("sizeof(INT64) != 8\n");
        return 0;
    }

		//ESDTABLECIENDO CARACTERISTICAS PARA LA EJECUCION
    cudaSetDevice( cutGetMaxGflopsDeviceId() );
		
		//INICIALIZACION DEL TIMER DE CONTROL DE TIEMPO
    cutilCheckError(cutCreateTimer(&hTimer));
	
		shrLog("Leer datos desde archivo...\n\n");
    NCiudades = 17;
		optimo = 2085;
    sizeArreglo = sizeof(int) * NCiudades;
    sizeMatriz  = sizeof(int) * NCiudades * NCiudades;

    shrLog("Allocating GPU memory...\n");
    cutilSafeCall( cudaMalloc((void **)&distancias_d, sizeMatriz)  );
    cutilSafeCall( cudaMalloc((void **)&w_d,          sizeArreglo) );
    cutilSafeCall( cudaMalloc((void **)&wPrima_d,     sizeArreglo) );

    shrLog("Allocating CPU memory...\n");
		distancias_h = (int *)malloc(sizeMatriz);
    w_h = (int *)malloc(sizeArreglo);

    shrLog("Initializing QRNG tables...\n\n");
    initQuasirandomGenerator(tableCPU);
    initTable_SM10(tableCPU);

    shrLog("Testing TSP...\n\n");

///////////////INICIO SECCION PARA VARIABLE/////////////////////////////
    cutilSafeCall( cudaMemset(w_d, 0, sizeArreglo) );//ESTO DEBERIA HACERLO EN EL KERNEL PARA 'W' Y 'WPRIMA'

		cutilSafeCall( cudaThreadSynchronize() );
		cutilCheckError( cutResetTimer(hTimer) );
		cutilCheckError( cutStartTimer(hTimer) );
		
    tsp_SM10(N, NCiudades, optimo, distancias_d, w_d, wPrima_d);

		shrLog("\nReading GPU results...\n");
    cutilSafeCall( cudaMemcpy(w_h, w_d, sizeArreglo, cudaMemcpyDeviceToHost) );

    shrLog("Mostrar solucion...\n");
		mostrar(NCiudades, w_h);

///////////////FIN SECCION PARA VARIABLE////////////////////////////////

    cutilSafeCall( cudaThreadSynchronize() );
    cutilCheckError(cutStopTimer(hTimer));
    gpuTime = cutGetTimerValue(hTimer);//(double)1e-3;

	  shrLogEx(LOGBOTH | MASTER, 0, "tsp, Time = %.5f s\n", gpuTime); 

    shrLog("Shutting down...\n");

    cutilCheckError(cutDeleteTimer(hTimer));
    free(distancias_h);
    free(w_h);
    cutilSafeCall( cudaFree(distancias_d) );
    cutilSafeCall( cudaFree(w_d) );
    cutilSafeCall( cudaFree(wPrima_d) );

    cudaThreadExit();
}
